// ======================================================================
// Hell Lua Bus ( Lua Bind) Lib
// 
// Copyright 2013 Hell-Prototypes
//
// http://code.google.com/p/hell-prototypes/
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
// ======================================================================

#include <usb.h> /* libusb header */
//#include <unistd.h> /* for geteuid */
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#include "define.h"

static int g_io_direction_cache;
static int g_io_data_cache;

static int port_index2bit_map[PIN_NUM] = {0, 1, 2, 3, 4, 5, 6, 7, 8+0, 8+1, 8+4, 8+5, 8+6};


static int l_12V_ctrl(lua_State *L)
{
    int bit_index = get_int_value(L);

    if(bit_index < 0) {
        lua_pushnumber (L, bit_index);
        return 1;
    }

    if(g_usb_dev != NULL) {
        if( usb_control_msg(g_usb_dev, 0x40, USB_12V_CTRL, bit_index, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }
    return 1;
}

static int l_io_bit_oe(lua_State *L)
{
    int bit_index, level;
    int top = lua_gettop(L);

    if(top != 2) {
        lua_pop(L, lua_gettop(L));//clear stack
        lua_pushnumber (L, -ERR_PARAM_NUM);
        return 1;
    }

    if(!lua_isnumber(L, -1)) {
        lua_pop(L, lua_gettop(L));//clear stack
        lua_pushnumber (L, -ERR_PARAM_TYPE);
        return 1;
    }
    level = lua_tointeger(L, -1);
    if(level < 0) {
        lua_pop(L, lua_gettop(L));//clear stack
        lua_pushnumber (L, -ERR_PARAM_TYPE);
        return 1;
    }

    if(!lua_isnumber(L, -2)) {
        lua_pop(L, lua_gettop(L));//clear stack
        lua_pushnumber (L, -ERR_PARAM_TYPE);
        return 1;
    }

    bit_index = lua_tointeger(L, -2);
    lua_pop(L, lua_gettop(L));//clear stack
    if(bit_index < 0) {
        lua_pushnumber (L, bit_index);
        return 1;
    }
    if(bit_index < PIN_NUM) {
        if(level) {
            g_io_direction_cache |= 1 << port_index2bit_map[bit_index];
        } else {
            g_io_direction_cache &= ~((int)(1 << port_index2bit_map[bit_index]));
        }
    } else {
        lua_pushnumber (L, -ERR_PARAM_VALUE);
        return 1;
    }

    if(g_usb_dev != NULL) {
        if( usb_control_msg(g_usb_dev, 0x40, USB_OE_SET, g_io_direction_cache, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;

}

static int l_io_port_oe(lua_State *L)
{
    int value = get_int_value(L);

    if(value < 0) {
        lua_pushnumber (L, value);
        return 1;
    }

    if(g_usb_dev != NULL) {
        g_io_direction_cache = value & 0xFF;
        if(value & (1 << 8)) {
			g_io_direction_cache |= 1<<port_index2bit_map[8];
		}
		if(value & (1 << 9)) {
			g_io_direction_cache |= 1<<port_index2bit_map[9];
		}
		if(value & (1 << 10)) {
			g_io_direction_cache |= 1<<port_index2bit_map[10];
		}
		if(value & (1 << 11)) {
			g_io_direction_cache |= 1<<port_index2bit_map[11];
		}
		if(value & (1 << 12)) {
			g_io_direction_cache |= 1<<port_index2bit_map[12];
		}
        
        if( usb_control_msg(g_usb_dev, 0x40, USB_OE_SET, g_io_direction_cache, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}

static int l_io_low_8bit_oe(lua_State *L)
{
    int value = get_int_value(L);

    if(value < 0) {
        lua_pushnumber (L, value);
        return 1;
    }

    if(g_usb_dev != NULL) {
        g_io_direction_cache =  (g_io_direction_cache & 0xff00) | (value & 0xFF);

        if( usb_control_msg(g_usb_dev, 0x40, USB_OE_SET, g_io_direction_cache, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}

static int l_io_wr_low_8bit(lua_State *L)
{
    int value = get_int_value(L);

    if(value < 0) {
        lua_pushnumber (L, value);
        return 1;
    }
    g_io_data_cache = (g_io_data_cache & 0xff00) | (value & 0xFF);

    if(g_usb_dev != NULL) {
        if( usb_control_msg(g_usb_dev, 0x40, USB_PORT_WR, g_io_data_cache, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}

static int l_io_write(lua_State *L)
{
    int value = get_int_value(L);

    if(value < 0) {
        lua_pushnumber (L, value);
        return 1;
    }
    g_io_data_cache = value & 0x00FF;
    
    if(value & (1 << 8)) {
		g_io_data_cache |= 1<<port_index2bit_map[8];
	}
	if(value & (1 << 9)) {
		g_io_data_cache |= 1<<port_index2bit_map[9];
	}
	if(value & (1 << 10)) {
		g_io_data_cache |= 1<<port_index2bit_map[10];
	}
	if(value & (1 << 11)) {
		g_io_data_cache |= 1<<port_index2bit_map[11];
	}
	if(value & (1 << 12)) {
		g_io_data_cache |= 1<<port_index2bit_map[12];
	}
    
    
    

    if(g_usb_dev != NULL) {
        if( usb_control_msg(g_usb_dev, 0x40, USB_PORT_WR, g_io_data_cache, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}
static int l_io_rd_low_8bit(lua_State *L)
{
	unsigned char buffer[8];
	int data;

    lua_pop(L, lua_gettop(L));//clear stack

    if(g_usb_dev != NULL) {
        if(	usb_control_msg(g_usb_dev, 0x40 | 0x80, USB_PORT_RD, 0x0000, 0x0000,
                                (char *)buffer, 2, 1000 ) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
        	data = buffer[0];
            lua_pushnumber (L, data);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}

static int l_io_read(lua_State *L)
{
	unsigned char buffer[8];
	int data;

    lua_pop(L, lua_gettop(L));//clear stack

    if(g_usb_dev != NULL) {
        if(	usb_control_msg(g_usb_dev, 0x40 | 0x80, USB_PORT_RD, 0x0000, 0x0000,
                                (char *)buffer, 2, 1000 ) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
        	data = buffer[0] + (buffer[1] << 8);
            lua_pushnumber (L, data);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}

static int l_io_setbit(lua_State *L)
{
    int bit_index = get_int_value(L);

    if(bit_index < 0) {
        lua_pushnumber (L, bit_index);
        return 1;
    }

    if(bit_index < PIN_NUM) {
        g_io_data_cache |= (1 << port_index2bit_map[bit_index]);
	} else {
        lua_pushnumber (L, -ERR_PARAM_VALUE);
        return 1;
    }

    if(g_usb_dev != NULL) {
        if( usb_control_msg(g_usb_dev, 0x40, USB_PORT_WR, g_io_data_cache, 0x0000,
                            NULL, 0, 1000) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }
    return 1;
}

static int l_io_clrbit(lua_State *L)
{
    int bit_index = get_int_value(L);

    if(bit_index < 0) {
        lua_pushnumber (L, bit_index);
        return 1;
    }

    if(bit_index < PIN_NUM) {
        g_io_data_cache &= ~(1 << port_index2bit_map[bit_index]);
	} else {
        lua_pushnumber (L, -ERR_PARAM_VALUE);
        return 1;
    }

    if(g_usb_dev != NULL) {
        if( usb_control_msg(g_usb_dev, 0x40, USB_PORT_WR, g_io_data_cache, 0x0000,
                            NULL, 0, 100) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
            lua_pushnumber (L, NO_ERR);
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }
    return 1;
}

static int l_io_getbit(lua_State *L)
{
	unsigned char buffer[8];
	int data;
    int bit_index = get_int_value(L);

    if(bit_index < 0) {
        lua_pushnumber (L, bit_index);
        return 1;
    }

    if(bit_index >= PIN_NUM) {
        lua_pushnumber (L, -ERR_PARAM_VALUE);
        return 1;
    }

    if(g_usb_dev != NULL) {
    	if(	usb_control_msg(g_usb_dev, 0x40 | 0x80, USB_PORT_RD, 0x0000, 0x0000,
                                (char *)buffer, 2, 1000 ) < 0) {
            lua_pushnumber (L, -ERR_CTRL_MSG);
        } else {
        	
        	data = buffer[0] + (buffer[1] << 8);
        	if(data & (1 << port_index2bit_map[bit_index])) {
                lua_pushboolean(L, 1);
            } else {
                lua_pushboolean(L, 0);
            }
        }
    } else {
        lua_pushnumber (L, -ERR_NO_OPEN);
    }

    return 1;
}
